// tspcluster - Traveling Salesman Problem
// Copyright (C) 2006  Frederik Carlier
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

using System;
using System.Collections.Generic;
using System.Text;

namespace UGent.Tsp
{
    /// <remarks>
    ///   This swapper does not use the <see cref="Count"/> property.
    /// </remarks>
    public class Swap311 : ISwapper
    {
        private const int DefaultCount = -1;

        #region Constructors
        public Swap311(Route route)
            : this(route, DefaultCount)
        {
        }

        public Swap311(Route route, int count)
        {
            this.route = route;
            this.subset = new Subset();
        }
        #endregion

        #region Properties

        // The two cities that will be swapped.
        private int first;
        private int last;
        private int proposed;
        private int accepted;
        private Subset subset;

        public int ProposedMoves
        {
            get { return proposed; }
        }

        public int AcceptedMoves
        {
            get { return accepted; }
        }

        private Route route;
        public IRoute Route
        {
            get { return route; }
        }

        public int Count
        {
            get
            {
                return -1;
            }
        }
        #endregion

        #region Methods
        public void Generate()
        {
            int[] values = new int[2];
            subset.Random(0, route.Map.CityCount - 1, 2, values);
            this.first = values[0];
            this.last = values[1];

            // Make sure that the values are always so that
            // the first city is on the left of the right city.
            if (first == 0 && last == route.Map.CityCount - 1)
            {
                this.first = values[1];
                this.last = values[0];
            }

            proposed++;
        }

        public void Do()
        {
            route.Swap(this.first, this.last);

            accepted++;
        }

        public double GetDelta()
        {
            if (first == last)
                return 0.0;

            double delta = 0;

            delta = delta - route.GetDistance(first - 1, first);
            delta = delta - route.GetDistance(last, last + 1);

            delta = delta + route.GetDistance(first - 1, last);
            delta = delta + route.GetDistance(first, last + 1);

            if (Cyclic.GetIndex(first + 1, Route.Map.CityCount) != last)
            {
                delta = delta - route.GetDistance(first, first + 1);
                delta = delta - route.GetDistance(last - 1, last);
                delta = delta + route.GetDistance(last, first + 1);
                delta = delta + route.GetDistance(last - 1, first);
            }

            return delta;
        }
        #endregion
    }
}
